package torres.action;

import java.util.ArrayList;
import java.util.Collection;

import torres.map.Location;
import torres.map.Map;
import torres.map.World;
import torres.entity.Entity;
import torres.fileIO.BasicContainer;
import torres.fileIO.ClassContainer;
import torres.fileIO.Saveable;

/**
 * @author Torres
 * Defines the attributes and effects of ... 						//TODO FIX LATER
 * 
 */
public abstract class Action implements Runnable, Saveable {
	
	private Effect elem;
	private Location location;
	private int range;
	private int power;
	private int accuracy;
	private int delay;
	private Map map;
	
	//TODO and what about having a modifiable decMultiplier?
	
	protected abstract Collection<Collection<Location>> getLocations();
	
	/**
	 * Creates a new Action
	 * @param elem Elemental type of Action
	 * @param range Range of Action
	 * @param power Damage power of Action
	 * @param accuracy Accuracy of Action
	 * @param delay Time delay of consecutive Action
	 */
	public Action(Effect elem, int range, int power, int accuracy, int delay) {
		this.setElement(elem);
		this.setRange(range);
		this.setPower(power);
		this.setAccuracy(accuracy);
		this.setDelay(delay);
		this.setMap(World.getInstance().getCurrentMap());
	}
	
	/**
	 * Creates a new Action
	 * @param elem Elemental type of Action
	 * @param range Range of Action
	 * @param location Location of the Action
	 * @param power Damage poewr of Action
	 * @param accuracy Accuracy of Action
	 * @param delay Time delay of consecutive Action 
	 */
	public Action(Effect elem, int range, Location location, int power, int accuracy, int delay) {
		this(elem, range, power, accuracy, delay);
		this.setLocation(location);
	}
	
	/**
	 * Calls Action to run
	 */
	public void execute() {
		Effect elem = this.getElement();
		int power = this.getPower();
		int accuracy = this.getAccuracy();
		double decMultiplier = 0.8;
		Collection<Collection<Location>> locs = this.getLocations();
		Collection<Location> prev = new ArrayList<Location>();
		//System.out.println("Class Action: Size of Collection: " + locs.size());

		for(Collection<Location> col : locs) {
			this.delay();
			for(Location l : col)
				this.getMap().applyEffect(l, elem, power, accuracy);
			for(Location l : prev)
				this.getMap().removeEffect(l);
			power *= decMultiplier;
			accuracy *= decMultiplier;
			prev = col;
		}
		this.delay();
		for(Location l : prev)
			this.getMap().removeEffect(l);
	}
	
	/**
	 * threads run behavior which executes Action
	 * @see java.lang.Runnable#run()
	 */
	public void run() {
		this.execute();
	}
	
	/**
	 * Sets the location of Action
	 * @param location the new location to set
	 */
	protected void setLocation(Location location) {
		this.location = location;
	}

	/**
	 * Returns the location of Action
	 * @return the location
	 */
	protected Location getLocation() {
		return location;
	}

	/**
	 * Sets the range of Action
	 * @param range the new range of Action to set
	 */
	protected void setRange(int range) {
		this.range = range;
	}

	/**
	 * Returns the range of Action
	 * @return the range
	 */
	protected int getRange() {
		return range;
	}

	/**
	 * Sets the power of Action
	 * @param power the new power of Action to set
	 */
	protected void setPower(int power) {
		this.power = power;
	}

	/**
	 * Returns the power of Action
	 * @return the power
	 */
	protected int getPower() {
		return power;
	}

	/**
	 * Sets the accuracy of Action
	 * @param accuracy the new accuracy to set
	 */
	protected void setAccuracy(int accuracy) {
		this.accuracy = accuracy;
	}

	/**
	 * Returns the accuracy of Action
	 * @return the accuracy
	 */
	protected int getAccuracy() {
		return accuracy;
	}

	/**
	 * Sets the elemental type of Action
	 * @param elem the new element of Action to set
	 */
	protected void setElement(Effect elem) {
		this.elem = elem;
	}

	/**
	 * Returns the element of Action
	 * @return the element type
	 */
	protected Effect getElement() {
		return elem;
	}

	/**
	 * Sets the delay of Action
	 * @param delay the new delay to set
	 */
	protected void setDelay(int delay) {
		this.delay = delay;
	}

	/**
	 * Returns the delay of Action
	 * @return the delay
	 */
	protected int getDelay() {
		return delay;
	}
	
	/**
	 * Delays Action by the value of delay
	 */
	protected void delay() {
		try{
			Thread.sleep(delay);
		}
		catch(InterruptedException e) {};
	}
	
	/*
	public void useAction(Location location) {
		System.out.println("Action Class: useAction()");		
		this.setLocation(location);
		this.execute();
		
	}
	*/
	
	/**
	 * Sets up the origin of Action 
	 * @param e new Entity used to setup origin of Action 
	 */
	public abstract void setUp(Entity e);
	
	/**
	 * Sets up the map of Action
	 * @param map new Map where Action occurs
	 */
	protected void setMap(Map map) {
		this.map = map;
	}
	
	/**
	 * Returns the map of Action 
	 * @return the map
	 */
	protected Map getMap() {
		return this.map;
	}
	
	/**
	 * Save function for Action for Save/Load.
	 * @see torres.fileIO.Saveable#save()
	 */
	@Override
	public ClassContainer save() {
		ClassContainer curClass = new ClassContainer("Action");
		if(location!=null)curClass.addProperty(location.save());
		curClass.addProperty(new BasicContainer("elem", elem));
		curClass.addProperty(new BasicContainer("range", range));
		curClass.addProperty(new BasicContainer("power", power));
		curClass.addProperty(new BasicContainer("accuracy", accuracy));
		curClass.addProperty(new BasicContainer("delay", delay));
		
		return curClass;
	}
	
	/**
	 * Save function for Action for Save/Load.
	 * @see torres.fileIO.Saveable#load(torres.fileIO.ClassContainer)
	 */
	@Override
	public void load(ClassContainer classContainer) {
		location = new Location(0,0);
		location.load((ClassContainer)classContainer.getProperty("Location"));
		BasicContainer elemCont = (BasicContainer)classContainer.getProperty("elem");
		elem = Effect.valueOf((String)elemCont.getValue());
		BasicContainer rangeCont = (BasicContainer)classContainer.getProperty("range");
		range = Integer.parseInt((String)rangeCont.getValue());
		BasicContainer powerCont = (BasicContainer)classContainer.getProperty("power");
		power = Integer.parseInt((String)powerCont.getValue());
		BasicContainer accuracyCont = (BasicContainer)classContainer.getProperty("accuracy");
		accuracy = Integer.parseInt((String)accuracyCont.getValue());
		BasicContainer delayCont = (BasicContainer)classContainer.getProperty("delay");
		delay = Integer.parseInt((String)delayCont.getValue());
		map = World.getInstance().getCurrentMap();
		
	}
	
}